/** 可改变大小的动态数组，默认在栈上建立指定大小的空间
 *  小于栈空间大小，使用栈空间，大于栈空间大小，则使用动态分配的堆空间
 * @file utils.hpp
 * @author Kiven Lee
 * @version 1.0
 * @date 2021-08-09
 *
 * @copyright Copyright (c) 2021
 *
 */

#pragma once

#include <cstddef>
#include <cassert>
#include <string>
#include <string_view>
#include <functional>
#include <utility>
#include "printf.hpp"

namespace klib {

    /** 空的string_view全局常量 */
    static inline constexpr std::string_view empty_sv = std::string_view();

    /** 指针类别结构，带指针大小
     *
     * @tparam T 指针类别模板
     */
    template<typename T = char>
    struct pointer_t {
        T*     data;
        size_t capacity;
    };


    /** 内存类别结构，带指针大小和指针容量
     *
     * @tparam T 指针类别模板
     */
    template<typename T = char>
    struct memory_t : pointer_t<T> {
        size_t size;
    };

    /** 智能指针类型，仿std::unique_ptr */
    template<typename T>
    struct smart_ptr_t {
        using value_type = T;

        T*       ptr;

        constexpr smart_ptr_t(): ptr(nullptr) {}
        explicit smart_ptr_t(T* src): ptr(src) {}

        template<typename... ARGS>
        explicit smart_ptr_t(std::in_place_type_t<T>, ARGS&&... args)
                : ptr(new T(std::forward<ARGS>(args)...)) {}

        ~smart_ptr_t() { if (ptr) delete ptr; }

        smart_ptr_t(const smart_ptr_t&) = delete;
        smart_ptr_t& operator =(const smart_ptr_t&) = delete;

        smart_ptr_t(smart_ptr_t&& src): ptr(src.ptr) { src.ptr = nullptr; }
        smart_ptr_t& operator =(smart_ptr_t&& src) {
            if (ptr) delete ptr;
            ptr = src.ptr;
            src.ptr = nullptr;
            return *this;
        }

        smart_ptr_t& operator =(T* src) {
            if (ptr) delete ptr;
            return *new (this) smart_ptr_t(src);
        }

        const T* get() const noexcept { return ptr; }
        T* get() noexcept { return ptr; }
        T* release() noexcept { T* r = ptr; ptr = nullptr; return r; }
        void reset() { if (ptr) { delete ptr; ptr = nullptr; } }

        T& operator *() { return *ptr; }
        T* operator ->() { return ptr; }
        operator T*() { return ptr; }

        operator bool() const noexcept { return ptr != nullptr; }
        bool operator !() const noexcept { return ptr == nullptr; }
        bool operator ==(const smart_ptr_t& src) const noexcept { return ptr == src.ptr; }
        bool operator ==(T* src) const noexcept { return ptr == src; }
        bool operator !=(const smart_ptr_t& src) const noexcept { return ptr != src.ptr; }
        bool operator !=(T* src) const noexcept { return ptr != src; }

    };

    /** 引用计数指针类型，仿std::shared_ptr */
    template<typename T>
    struct ref_ptr_t {
        using value_type = T;

        struct data_t {
            T*     ptr;
            size_t owner: 1;
            size_t count: sizeof(size_t) * 8 - 1;
        } *data;

    public:
        constexpr ref_ptr_t(): data(nullptr) {}
        explicit ref_ptr_t(T* src): data(new data_t{src, 0, 1}) {}

        template<typename... ARGS>
        explicit ref_ptr_t(std::in_place_type_t<T>, ARGS&&... args) {
            data = (data_t*) new uint8_t[sizeof(data_t) + sizeof(T)];
            data->ptr = (T*) (data + 1);
            data->owner = 1;
            data->count = 1;
            new (data->ptr) T(std::forward<ARGS>(args)...);
        }

        ~ref_ptr_t() {
            if (data && !--data->count) {
                if (data->owner) data->ptr->~T();
                else delete data->ptr;
                delete[] (uint8_t*) data;
            }
        }

        ref_ptr_t(const ref_ptr_t& src): data(src.data) {
            if (data) ++(data->count);
        }

        ref_ptr_t& operator =(const ref_ptr_t& src) {
            this->~ref_ptr_t();
            return *new (this) ref_ptr_t(src);
        }

        ref_ptr_t(ref_ptr_t&& src): data(src.data) { src.data = nullptr; }

        ref_ptr_t& operator =(ref_ptr_t&& src) {
            this->~ref_ptr_t();
            return *new (this) ref_ptr_t(std::move(src));
        }

        ref_ptr_t& operator =(T* src) {
            this->~ref_ptr_t();
            return *new (this) ref_ptr_t(src);
        }

        const T* get() const noexcept { return data ? data->ptr : nullptr; }
        T* get() noexcept { return data ? data->ptr : nullptr; }
        size_t ref_count() const noexcept { return data ? data->count : 0; }
        bool owner() const noexcept { return data ? data->owner : false; }
        void reset() { this->~ref_ptr_t(); data = nullptr; }

        T& operator *() { return *get(); }
        T* operator ->() { return get(); }
        operator T*() { return get(); }

        operator bool() const noexcept { return data && data->ptr; }
        bool operator !() const noexcept { return !data || !data->ptr; }
        bool operator ==(const ref_ptr_t& src) const noexcept { return data == src.data; }
        bool operator !=(const ref_ptr_t& src) const noexcept { return data != src.data; }
        bool operator ==(T* src) const noexcept {
            return data && data->ptr == src || (!data || !data->ptr) && !src;
        }
        bool operator !=(T* src) const noexcept {
            return !this->operator==(src);
        }

    };


    /** 创建智能指针对象，直接调用类型T的构造函数
     *
     * @tparam T 智能指针对象指向的类型
     * @tparam A 类型T的构造函数参数类型
     * @param args 类型T的构造函数参数
     * @return smart_ptr_t<T>
     */
    template<typename T, typename... A>
    inline smart_ptr_t<T> make_smart_ptr(A&&... args) {
        return smart_ptr_t<T>(new T(std::forward<A>(args)...));
    }


    /** 对字符串进行字符的全局替换
     *
     * @param inout 字符串缓冲区，在此缓冲区进行替换
     * @param from 查找的字符
     * @param to 替换的字符
     * @return char* inout 字符串缓冲区
     */
    inline void str_replace_all(char* inout, char from, char to) {
        char *p = inout;
        while ((p = std::strchr(p, from))) *p++ = to;
    }


    /** 模拟golang的defer操作，定义后，会在当前函数退出前自动调用，利用了C++的RAII功能
     *
     * @tparam ARGS 不定参数，将被合成std::tuple作为回调函数的参数被调用
     *
     * example
     *     std::string s("1234");
     *     defer_t<std::string> defer1([](auto& a){ printf("%s\n", a.c_str()); }, std::move(s));
     *     defer_t<void> defer3(std::bind(printf, "%s\n", s.c_str()));
     */
    template<typename A>
    struct defer_t {
        using func_t = std::function<void (A&)>;
        func_t _func;
        A      _arg;
        defer_t(const func_t& func, const A& arg): _func(func), _arg(arg) {}
        defer_t(const func_t& func, A&& arg): _func(func), _arg(std::move(arg)) {}
        ~defer_t() { _func(_arg); }
    };

    /** 无参数的模板偏特化 */
    template<>
    struct defer_t<void> {
        std::function<void ()> _func;
        defer_t(const std::function<void ()>& func): _func(func) {}
        ~defer_t() { _func(); }
    };


    /** 可变更大小的动态数组类，适用于局部变量分配栈内存，运行时动态计算超出时使用堆内存的情况，
     *  在这种情况下，使用本类可尽可能减少堆内存分配次数，从而减少内存碎片。
     *  例如：某个函数需要动态内存进行临时数据保存，大部分情况不超过500个（但偶尔也有超出的时候），
     *  这个时候应该做如此声明：
     *     void output(char* name, char* address) {
     *         dynarray<char, 200> buf(strlen(name) + strlen(address) + 1);
     *         strcpy(buf.data(), name);
     *         strcat(buf.data(), address);
     *         puts(buf.data());
     *     }
     *
     * @tparam T 动态数组类型
     * @tparam _Num 初始容量大小
     */
    template<typename T, size_t _Num>
    class darray_t {

    public:
        typedef T  value_type;
        typedef T* iterator;

    protected:
        T*     _heap;
        size_t _size;
        T      _stack[_Num];

    public:
        darray_t(): _heap(_stack), _size(_Num) { static_assert((_Num & (_Num - 1)) == 0, "_Num must be power of 2"); }

        darray_t(size_t size): _size(size) {
            static_assert((_Num & (_Num - 1)) == 0, "_Num must be power of 2");
            _heap = size > _Num ? new T[size] : _stack;
        }

        ~darray_t() { if (_heap != _stack) delete[] _heap; }

        darray_t(const darray_t&) = delete;
        darray_t(darray_t&&) = delete;
        darray_t& operator = (const darray_t&) = delete;
        darray_t& operator = (darray_t&&) = delete;
        void* operator new (size_t) = delete;

        /** 返回指向内容数组的第一个元素的指针 */
        T* data() noexcept { return const_cast<T*>(_heap); }
        const T* data() const noexcept { return _heap; }

        // 迭代器
        T* begin() noexcept { return data(); }
        const T* begin() const noexcept { return data(); }
        T* end() noexcept { return data() + size(); }
        const T* end() const noexcept { return data() + size(); }

        std::reverse_iterator<T*> rbegin() noexcept {
            return std::reverse_iterator<T*>(end());
        }
        std::reverse_iterator<const T*> rbegin() const noexcept {
            return std::reverse_iterator<const T*>(end());
        }

        std::reverse_iterator<T*> rend() noexcept {
            return std::reverse_iterator<T*>(begin());
        }
        std::reverse_iterator<const T*> rend() const noexcept {
            return std::reverse_iterator<const T*>(begin());
        }

        void fill(const T& v) { for (T* p = begin(), *pe = end(); p < pe; ++p) *p = v; }

        /** 返回动态数组容量 */
        size_t size() const noexcept { return _size; }

        /** 返回动态数组容量 */
        size_t max_size() const noexcept { return _size; }

        /** 检查容器是否为空 */
        bool empty() const noexcept { return _size == 0; }

        /** 访问指定的元素, 不做越界检查 */
        T& operator[](size_t idx) noexcept { return data()[idx]; }
        const T& operator[](size_t idx) const noexcept { return data()[idx]; }

        /** 访问指定的元素，同时做越界检查，越界时将抛出异常 */
        T& at(size_t idx) {
            if (idx >= _size) throw "dynarray.at(idx) error: idx out of range";
            return const_cast<char&>(data()[idx]);
        }
        const T& at(size_t idx) const {
            if (idx >= _size) throw "dynarray.at(idx) error: idx out of range";
            return data()[idx];
        }

        /** 访问第一个元素 */
        T& front() noexcept { return *begin(); }
        const T& front() const noexcept { return *begin(); }

        /** 访问最后一个元素 */
        T& back() noexcept { return *(end() - 1); }
        const T& back() const noexcept { return *(end() - 1); }

        /** 重新设置容量，如果引起重新分配内存，将复制已有数据到新内存
         *
         * @param size 新容量
         * @param keep 是否保留旧数据
         */
        void resize(size_t size, bool keep = false) {
            if (size > _size) {
                T* new_heap = new T[size];
                if (keep) std::copy(_heap, _heap + _size, new_heap);
                if (_heap != _stack) delete[] _heap;
                _heap = new_heap;
                _size = size;
            }
        }

        /** 恢复原始长度，释放从堆中分配的空间 */
        void reset() {
            if (_heap != _stack) {
                delete[] _heap;
                _heap = _stack;
            }
            _size = _Num;
        }

        /** 获取系统推荐的长度
         *
         * @param require_size 需要的长度
         * @return size_t 系统推荐的长度
         */
        size_t recommend_size(size_t require_size) const noexcept {
            assert((_size & (_size - 1)) == 0);
            size_t n = _size;
            while (n < require_size) n <<= 1;
            return n;
        }
    };


    /** 字符串类别的动态大小类，适用于作为函数局部变量格式化字符串使用
     *
     * @tparam _Num 初始容量大小
     */
    template<size_t _Num = 256>
    class dstring_t : public darray_t<char, _Num> {

    protected:
        size_t _len;

    public:
        dstring_t():_len(0) {}

        dstring_t(size_t size): darray_t<char, _Num>(size), _len(0) {}

        template<typename... ARGS>
        dstring_t(const char* fmt, ARGS&&... args) {
            printf(0, fmt, std::forward<ARGS>(args)...);
        }

        /** 获取当前写入位置 */
        char* position() noexcept { return this->_heap + _len; }

        /** 获取字符串长度 */
        size_t length() const noexcept { return _len; }

        /** 设置字符串长度 */
        void set_length(size_t len) {
            if (len >= this->_size)
                this->resize(this->recommend_size(len + 1), true);
            this->_heap[len] = '\0';
            _len = len;
        }

        void reset() {
            darray_t<char, _Num>::reset();
            this->_heap[0] = '\0';
            _len = 0;
        }

        void fill(char v) {
            std::memset(this->_heap, v, this->_size);
            this->_heap[this->_size - 1] = '\0';
        }

        void putchar(char c) {
            if (_len >= this->_size)
                this->resize(this->recommend_size(_len + 2), true);
            char* p = this->_heap + _len;
            _len += 1;
            *p++ = c;
            *p = '\0';
        }

        /** 调用snprintf进行格式化字符串并写入（如果需要将自动重新分配内存）
         *
         * @param fmt 格式化模板
         * @param args 格式化参数
         */
        void printf(const char* fmt, ...) {
            va_list va;
            va_start(va, fmt);
            vprintf(_len, fmt, va);
            va_end(va);
        }

        /** 调用snprintf进行格式化字符串并写入（如果需要将自动重新分配内存）
         *
         * @param pos 起始的写入位置
         * @param fmt 格式化模板
         * @param args 格式化参数
         */
        void printf(size_t pos, const char* fmt, ...) {
            va_list va;
            va_start(va, fmt);
            vprintf(pos, fmt, va);
            va_end(va);
        }

        /** 调用snprintf进行格式化字符串并写入（如果需要将自动重新分配内存）
         *
         * @param fmt 格式化模板
         * @param va 参数列表
         */
        void vprintf(const char* fmt, va_list va) {
            vprintf(_len, fmt, va);
        }

        /** 调用snprintf进行格式化字符串并写入（如果需要将自动重新分配内存）
         *
         * @param pos 起始的写入位置
         * @param fmt 格式化模板
         * @param va 参数列表
         */
        void vprintf(size_t pos, const char* fmt, va_list va) {
            assert(fmt != nullptr);
            va_list local_va;
            va_copy(local_va, va);
            size_t len = klib::vsnprintf(nullptr, (size_t) -1, fmt, local_va) + 1;
            size_t new_size = pos + len;

            if (new_size > this->_size)
                this->resize(this->recommend_size(new_size), pos != 0);

            klib::vsnprintf(this->_heap + pos, len, fmt, va);
            _len = new_size - 1;
        }

        /** 写入内存内容
         *
         * @param data 待写入的数据
         * @param size 写入长度
         */
        void write(const char* data, size_t size) {
            write(_len, data, size);
        }

        /** 写入内存内容
         *
         * @param pos 起始写入位置
         * @param data 待写入的数据
         * @param size 写入长度
         */
        void write(size_t pos, const char* data, size_t size, bool terminal = true) {
            if (data && size) {
                size_t new_size = pos + size;
                if (terminal) ++new_size;
                if (new_size > this->_size)
                    this->resize(this->recommend_size(new_size), pos != 0);
                std::memcpy(this->_heap + pos, data, size);
                _len = new_size;
                if (terminal) this->_heap[--_len] = '\0';
            }
        }

        std::string to_string() const { return std::string(this->_heap, _len); }
        std::string_view to_string_view() const { return std::string_view(this->_heap, _len); }

    };


    class dmem_t {

    private:
        uint8_t* _data;
        size_t   _size;
        size_t   _cap;

        void _clear() {
            _data = nullptr;
            _size = 0;
            _cap = 0;
        }

        static size_t _align_size(size_t current, size_t require) {
            while (current < require) current <<= 1;
            return current;
        }

        void _glow(size_t cap) {
            assert((cap & (cap - 1)) == 0);
            uint8_t* new_data = new uint8_t[cap];
            if (_data) {
                std::memcpy(new_data, _data, _size);
                delete _data;
            }
            _data = new_data;
            _cap = cap;
        }

    public:
        dmem_t():_data(nullptr), _size(0), _cap(0) {}

        dmem_t(size_t cap): _size(0) {
            _cap = _align_size(64, cap);
            _data = new uint8_t[_cap];
        }

        dmem_t(const void* data, size_t size): dmem_t(size) {
            std::memcpy(_data, data, size);
        }

        ~dmem_t() { if (_data) delete _data; }

        dmem_t(const dmem_t&) = delete;
        dmem_t& operator=(const dmem_t&) = delete;

        dmem_t(dmem_t&& src):_data(src._data), _size(src._size), _cap(src._cap) { src._clear(); }
        dmem_t& operator=(dmem_t&& src) { return *new (this) dmem_t(std::move(src)); }

        uint8_t* data() noexcept { return _data; }
        const uint8_t* data() const noexcept { return _data; }
        char* cdata() { return (char*) data(); }
        const char* cdata() const noexcept { return (char*) data(); }

        size_t size() const { return _size; }
        size_t capacity() const { return _cap; }

        void putu8(uint8_t c) {
            if (_size >= _cap)
                _glow(_cap << 1);
            ((uint8_t*) _data)[_size++] = c;
        }

        void putc(char c) { putu8((uint8_t) c); }

        void vprintf(const char* fmt, va_list va) {
            va_list va_local;
            va_copy(va_local, va);
            size_t len = klib::vsnprintf(nullptr, (size_t)-1, fmt, va_local) + 1;
            if (_size + len > _cap)
                _glow(_align_size(_cap, _size + len));
            klib::vsnprintf((char*) _data + _size, len, fmt, va);
            _size += len - 1;
        }

        void printf(const char* fmt, ...) {
            va_list va;
            va_start(va, fmt);
            this->vprintf(fmt, va);
            va_end(va);
        }

        /** 写入到内存缓冲区
         *
         * @param src 数据源地址
         * @param size 写入长度
         */
        dmem_t& write(const void* src, size_t size) {
            size_t ns = _size + size;
            if (ns > _cap)
                _glow(_align_size(_cap, ns));
            std::memcpy(_data + _size, src, size);
            _size = ns;
            return *this;
        }

        /** 从缓冲区读取内容到指定位置
         *
         * @param pos 缓冲区读取偏移
         * @param dst 目标位置
         * @param size 读取长度
         * @return true: 成功，false: 失败
         */
        bool read(std::size_t pos, void* dst, std::size_t size) const {
            if (pos + size > _size)
                return false;
            std::memcpy(dst, _data + pos, _size);
            return true;
        }

        /** 重新设置长度, 注意，如果是扩展长度，则新扩展的内容为不可预期 */
        void resize(std::size_t size) {
            if (size > _cap)
                _glow(_align_size(_cap, size));
            _size = size;
        }

        /** 重新设置容量， 注意，只扩大不缩小，小于原先容量将被忽略 */
        void recapacity(std::size_t capacity) {
            if (capacity > _cap)
                _glow(_align_size(_cap, capacity));
        }

        /** 重置缓冲区，全部数据清零并释放内存 */
        void reset() {
            if (_data) {
                delete _data;
                _data = nullptr;
                _size = 0;
                _cap = 0;
            }
        }
    };

} // namespace klib
